package com.wjw.java.thread;

/**
 * 线程同步机制：
 * ------------------------------------------------------------------------------------
 * 1.在多线程中,一些敏感的数据不能同时被多个线程操作,此时就需要用线程同步技术,
 * 保证数据在任何时候,最多只有一个线程操作,以保证数据的完整性
 * 2.也可以这样理解,线程同步,即当有一个线程对内存进行操作时,其他线程都不能对该内存地址进行操作
 * 直到该线程操作完,其他线程才能对该内存地址进行操作
 * ------------------------------------------------------------------------------------
 * 线程同步的具体方法：
 * ------------------------------------------------------------------------------------
 * 1.同步代码块
 * synchronized (对象){//锁可以是this,也可以是其他对象(要求是同一个对象)
 *      //需要被同步的代码
 * }
 *
 * 2.同步方法：锁可以是this,也可以是其他对象(要求是同一个对象)
 * public void synchronized method(){
 *     //需要被同步的代码
 * }
 *
 * 3.静态同步方法：锁为当前类本身,类名.class
 * public static void synchronized method(){
 *     //要被同步的代码
 * }
 * ------------------------------------------------------------------------------------
 * 继承Thread和实现Runnable接口锁对象的区别：
 * ------------------------------------------------------------------------------------
 * 1.
 * class A extend Thread{
 *     private Object lock = new Object();
 *     public void run() {...}
 * }
 * A a1 = new A();
 * A a2 = new A();
 * A a3 = new A();
 * a1.start();
 * a2.start();
 * a3.start();
 *
 * 此时a1、a2、a3的this都是他们自己,而非同一个
 * 此时的lock(对象锁)也是a1、a2、a3他们各自的lock,而非同一个,
 * 此时：锁为this或锁为lock,都不能保证线程安全
 *      锁为A.class(类名.class),能保证线程安全
 *
 * 2.
 * class B implements Runnable{
 *    private Object lock = new Object();
 *    public void run() {...}
 * }
 * B b = new B();
 * Thread b1 = new Thread(b);
 * Thread b2 = new Thread(b);
 * Thread b3 = new Thread(b);
 * b1.start();
 * b2.start();
 * b3.start();
 *
 * 此时b1、b2、b3的this是同一个,即同一个b对象
 * 此时的lock(对象锁)也是同一个,即b对象中的lock
 * 此时：锁为this或锁为lock或锁为B.class(类名.class),能保证线程安全
 * 所以推荐使用实现Runnable接口的方式,创建线程
 * 具体代码见：Synchronized_ 和 Synchronized_2
 */
public class Synchronized_ {
    public static void main(String[] args) {

        //使用实现Runnable接口,共用一个实现类
        SellTicketSafe sellTicket = new SellTicketSafe();
        //此时t1、t2、t3线程传入的都是同一个sellTicket类,
        //所以锁this、锁同一个其他对象、锁类名.class都能保证线程安全
        Thread t1 = new Thread(sellTicket,"窗口A：");
        Thread t2 = new Thread(sellTicket,"窗口B：");
        Thread t3 = new Thread(sellTicket,"窗口C：");

        t1.start();
        t2.start();
        t3.start();
    }
}
//使用实现Runnable接口
class SellTicketSafe implements Runnable{
    //使用static修饰,共享数据
    private static int ticketNum = 99;
    //是否循环
    private static boolean ifLoop = true;

    //同步方法：锁可以是this,也可以是其他对象(要求是同一个对象)
    private Object lock = new Object();

    @Override
    public void run() {
        while (ifLoop){
            //sell();//同步代码块：锁为this
            //sell2();//同步代码块：锁为同一其他对象
            //sell3();//同步方法：锁为this
            //sell4();//静态同步方法：锁为类名.class 即：SellTicketSafe.class
            sell5();//静态同步方法：锁为类名.class 即：SellTicketSafe.class
        }
    }

    //1.使用同步代码块：锁为this
    public void sell(){
        synchronized (this){
            if(ticketNum <= 0){
                System.out.println("没票了,停止售票...");
                ifLoop = false;
                return;
            }
            String name = Thread.currentThread().getName();
            try {
                Thread.sleep(100);
                System.out.println(name +"售出一张票,剩余:"+ (-- ticketNum) +"张票..");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //2.使用同步代码块：锁为同一其他对象
    public void sell2(){
        synchronized (lock){
            if(ticketNum <= 0){
                System.out.println("没票了,停止售票...");
                ifLoop = false;
                return;
            }
            String name = Thread.currentThread().getName();
            try {
                Thread.sleep(100);
                System.out.println(name +"售出一张票,剩余:"+ (-- ticketNum) +"张票..");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //3.同步方法：锁为this
    public synchronized void sell3(){
        if(ticketNum <= 0){
            System.out.println("没票了,停止售票...");
            ifLoop = false;
            return;
        }
        String name = Thread.currentThread().getName();
        try {
            Thread.sleep(100);
            System.out.println(name +"售出一张票,剩余:"+ (-- ticketNum) +"张票..");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //4.静态同步方法：锁为类名.class 即：SellTicketSafe.class
    public static synchronized void sell4(){
        if(ticketNum <= 0){
            System.out.println("没票了,停止售票...");
            ifLoop = false;
            return;
        }
        String name = Thread.currentThread().getName();
        try {
            Thread.sleep(100);
            System.out.println(name +"售出一张票,剩余:"+ (-- ticketNum) +"张票..");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //5.静态同步方法：锁为类名.class 即：SellTicketSafe.class
    public static void sell5(){
        synchronized(SellTicketSafe.class){
            if(ticketNum <= 0){
                System.out.println("没票了,停止售票...");
                ifLoop = false;
                return;
            }
            String name = Thread.currentThread().getName();
            try {
                Thread.sleep(100);
                System.out.println(name +"售出一张票,剩余:"+ (-- ticketNum) +"张票..");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}


